package com.shi.aspect;

import com.shi.annotation.log.MyLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Component
@Aspect
@Slf4j
public class LogAspect {

    @Pointcut("@annotation(com.shi.annotation.log.MyLog)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object handle(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String requestURI = request.getRequestURI();
        Object[] args = joinPoint.getArgs();
        MyLog annotation = method.getAnnotation(MyLog.class);
        String logDesc = parseSpEL(joinPoint);
        long begin = System.currentTimeMillis();
        Object res = null;
        try {
            res = joinPoint.proceed(args);
        } catch (Throwable e) {
            throw new RuntimeException();
        }
        long end = System.currentTimeMillis();
        // 可以定义entity 持久化到数据库
        log.info("日志类型：{}\t日志描述：{}\t请求URI：{}\t方法签名：{}\t请求参数列表：{}\t请求用时(ms)：{}", annotation.type().getCode(), logDesc, requestURI, methodSignature, args, end - begin);
        return res;
    }

    /**
     * 解析 @MyLog 注解中的 desc属性
     */
    private String parseSpEL(ProceedingJoinPoint joinPoint) {
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        MyLog annotation = method.getAnnotation(MyLog.class);
        if (annotation.desc() == null || "".equals(annotation.desc().trim())) {
            return "";
        }
        Object[] args = joinPoint.getArgs();
        Parameter[] parameters = method.getParameters();
        // 向SpEL上下文注入参数信息
        for (int i = 0; i < parameters.length; i++) {
            context.setVariable(parameters[i].getName(), args[i]);
        }
        // 使用解析器，在上下文中解析目标表达式
        Object value = parser.parseExpression(annotation.desc()).getValue(context);
        if (value == null) {
            return "";
        }
        return value.toString();
    }

}
